require 'yaml'
require 'set'

class State
  attr_accessor :has_critical
  attr_accessor :critical_requested
  attr_accessor :granted
  attr_accessor :seen

  def initialize
    @has_critical = false
    @critical_requested = false
    @granted = Set.new
    @seen = false
  end

  def reset
    @has_critical = false
    @critical_requested = false
    @granted = Set.new
  end

  def critical
    @has_critical = true
    @critical_requested = false
    @granted = Set.new
  end
end

class Node
  attr_reader :id
  attr_reader :ip
  attr_reader :port
  attr_accessor :clock
  attr_accessor :zombie
  attr_accessor :pending

  def initialize label, ip, port
    @id = label
    @ip = ip
    @port = port
    @clock = 1
    @zombie = false

    @state = State.new

    @pending = []
  end


  def == node
    node.id.to_s == @id.to_s
  end

  def inspect
    "Node ID#{@id} #{@ip}:#{@port} Zombie: #{zombie}"
  end

  def to_s
    inspect
  end
end


class NodeManager
  include Enumerable

  def initialize
    @nodes = []
  end

  # Load nodes from config
  def load filename
    config = YAML.load_file(filename)
    @nodes = config['nodes'].to_a.map do |c|
      Node.new c[0], c[1]['ip'], c[1]['port']
    end
  end

  def clocks
    clocks = {}
    @nodes.each do |n|
      clocks[n.id] = n.clock
    end

    clocks
  end

  def get id
    @nodes.select { |n| n.id.to_s == id.to_s }.first
  end

  def each
    @nodes.each { |n| yield n }
  end

  def inspect
    @nodes.each { |n| puts n }
  end

  def to_s
    inspect
  end

  def ids
    @nodes.select { |n| !n.zombie }.map { |n| n.id }.to_set
  end

  def zombies
    @nodes.select { |n| n.zombie }
  end

  def active 
    @nodes.select { |n| !n.zombie }
  end

  def except *exceptions
    @nodes.select { |n| !exceptions.include? n }
  end

  def all?
    zombies.empty?
  end

  def none?
    zombies.size >= @nodes.size - 1
  end
end
